/*
 * BufferManager.cpp
 *
 * Created on: 09-Oct-2009
 * Author: Atul Shukla (atul.shukla@iiitb.net)
 */
#include<iostream>
#include<stdio.h>
#include"BufferManager.h"
#include<vector>
#define FLAG 0

BufferManager::BufferManager(Database* db1, int numBuff, Status& status,
		char* replacementPolicy) {

	//inititalise Replacementpolicy
	buffPool = new Page[numBuff];
	if (buffPool == NULL) {
		status = error;
		return;
	}
	hit = 0;
	accessCount = 0;
	size = numBuff;
	rp = new ReplacementPolicy(buffPool, numBuff);
	databaseName = db1->getName().c_str();
	db = db1;
	vector<int> k;
}
BufferManager::~BufferManager() {

	flushAllPages();
	if (buffPool != NULL) {
		delete[] buffPool;
		buffPool = NULL;
	}
	if (db != NULL) {
		db->~Database();
		db = NULL;
	}
}
Status BufferManager::close() {
	flushAllPages();
	if (buffPool != NULL) {
		delete[] buffPool;
		buffPool = NULL;
	}
	if (db != NULL) {
		db->~Database();
		db = NULL;
	}
	return ok;
}
Status BufferManager::newPage(int& firstPageNo, Page*& firstPage, int howMany) {
	if (db != NULL) {
		if (db->allocatePage(firstPageNo) == error) {//allocate on disk
			return error;
		}
		if (pinPage(firstPageNo, firstPage) == error) {
			db->deallocatePage(firstPageNo);
			firstPageNo = -1;
			firstPage = '\0';
			return error;
		}
		return ok;
	} else
		return error;
}

Status BufferManager::pinPage(int pageno, Page*& page, int emptyPage) {
	accessCount++;
	it = pagemap.find(pageno);
	if (it != pagemap.end()) { //if found
		if (page->pincount == 0) { // now no more eligible for replacement

		}
		hit++;
		page->pincount++;
		page = &buffPool[pagemap[pageno]];//retrive pageindex from map corresponding to pageno
		rp->update(pagemap[pageno]);
		//cout << "Page Found in Frame# ::" << it->second;
		return ok;
	} else {//not found

		int i = rp->getIndexOfFrame();//get frame
		if(buffPool[i].pageno!=-1){
			it = pagemap.find(buffPool[i].pageno);
			pagemap.erase(buffPool[i].pageno);
		}
		if (i >= 0) {
			//Must also write out the old page in the chosen frame if it is dirty
			// before reading new page.
			if (buffPool[i].pageno > -1 && buffPool[i].dirty > 0) {
				db->writePage(buffPool[i].pageno, &buffPool[i]);
			}

			db->readPage(pageno, &buffPool[i]);//read page in buffer

			page = &buffPool[i];//pointer for returning
			page->pincount++;//pin it

			pagemap[pageno] = i;//map frame to pageno
			rp->update(i);
			//cout << "Frame# Assigned/Replaced ::" << i;
			return ok;
		} else {//frame is not available
			return error;
		}
	}
	return ok;
}

Status BufferManager::unpinPage(int pageno, int dirty) {
	int frame = pagemap[pageno];
	if (buffPool != NULL && buffPool[frame].pincount > 0) { //if found in buffer

		if (dirty == 1) {//if requested for dirty
			buffPool[frame].dirty = dirty;
		}
		buffPool[frame].pincount--;
		return ok;
	} else
		return error;//page not exist in buffer
}

Status BufferManager::freePage(int pageNo) {
	if (db != NULL) {
		db->deallocatePage(pageNo);
		return ok;
	} else {
		return error;
	}
}

Status BufferManager::flushPage(int pageNo) {
	if (db != NULL) {
		db->writePage(pageNo, &buffPool[pagemap[pageNo]]);
		if (buffPool != NULL)
			buffPool[pagemap[pageNo]].dirty = 0; //written , dirty bit 0
		else
			return error;
		return ok;
	} else
		return error;
}

Status BufferManager::flushAllPages() {
	if (db != NULL) {
		for (it = pagemap.begin(); it != pagemap.end(); ++it) {
			db->writePage(it->first, &buffPool[it->second]);//first is pageno  second is frame no
			if (buffPool != NULL)
				buffPool[it->second].dirty = 0; //written , dirty bit 0
			else
				return error;

		}
		db->saveState();
		return ok;
	} else
		return error;
}

void BufferManager::viewFrame(int frameNo) {
	if (buffPool != NULL && buffPool[frameNo].pageData != NULL) {
		//Page a = buffPool[frameNo];
		int j;
		for (int i = 0; i < PAGE_SIZE; i += 16) {
			for (j = 0; j < 16; j++)
				printf("%02x ", buffPool[frameNo].pageData[i + j]);
			cout << " ";
			for (j = 0; j < 16; j++)
				if (isprint(buffPool[frameNo].pageData[i + j]))
					cout << buffPool[frameNo].pageData[i + j];
			printf("\n");
		}
	}
}
void BufferManager::viewBufferStatus(){
	if (db != NULL) {
		int count=0;
		for (it = pagemap.begin(); it != pagemap.end(); ++it) {
			count++;
			//db->writePage(it->first, &buffPool[it->second]);//first is pageno  second is frame no
			//cout<<"Frame no.: "<<it->second<<" Page No.: "<<it->first;
			//cout<<" Pincount: "<<buffPool[it->second].pincount;
			//cout<<" Dirty Flag: "<<buffPool[it->second].dirty<<endl<<endl;;
		}
		cout<<"Total frames ::"<<pagemap.size();
	}
}
double BufferManager::getHitRatio() {
	return hit / accessCount;
}

